package com.scyshkj.university.wechat.pay;

import com.scyshkj.university.wechat.utils.MD5Utils;
import com.scyshkj.university.wechat.utils.ParameterToMap;
import com.scyshkj.university.wechat.utils.XmlUtil;
import com.scyshkj.university.wechat.wanxiao.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.JAXBException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by 唐旭 on 2016/11/21.
 */
@Service
@Transactional
public class WechatPayServiceImpl implements WechatPayService {

    @Autowired
    private RestTemplate template;
    @Autowired
    private WanxiaoPayService wanxiaoPayService;
    @Autowired
    private WechatProperties properties;
    @Autowired
    private RechargeRepository rechargeRepository;
    @Autowired
    private WanxiaoAccessTokenRepository tokenRepository;
    @Autowired
    private WanxiaoPayApplyResultRepository resultRepository;

    @Override
    public RechargeResultDto pay(RechargeFormDto formDto) {
        //1,玩校发起支付,获得充值流水号
        BigDecimal amount = new BigDecimal(formDto.getAmount());
        PayApplyResult applyResult = wanxiaoPayService.payApply(amount);
        if (applyResult.isError()) {
            return new RechargeResultDto(applyResult.getReturnMessage());
        }
        //2,微信统一下单(统一下单中设置一卡通卡号,充值流水号,在微信通知服务器的时候方便使用)
        WechatUnifiedOrderRequestParameter parameter =
                createUnifiedorderRequestParameter(amount, applyResult.getUuid());
        WechatUnifiedOrderResult result;
        try {
            result = this.unifiedOrder(parameter);
        } catch (JAXBException e) {
            e.printStackTrace();
            return new RechargeResultDto("预定下单出现错误,请重试");
        }
        //3,存储各参数
        if (result != null && result.isSuccess()) {
            //3.1存储玩校
            resultRepository.save(applyResult);
            //3.2存储微信统一下单
            parameter.setAccessToken(tokenRepository.getToken().getAccessToken());
            rechargeRepository.save(parameter);
        }
        //4,跳转,发起支付
        return buildResult(result);
    }

    @Override
    public String rechargeNotify(HttpServletRequest request) throws IOException {
        String notifyXml = readXmlFromStream(request);
        String outTradeNo = findOutTradeNo(notifyXml);
        String attach = findAttach(notifyXml);

        WechatUnifiedOrderRequestParameter byOutTradeNo = rechargeRepository.findByOutTradeNo(outTradeNo);
        //充值未完成
        if (!byOutTradeNo.isComplete()) {
            PayApplyResult one = resultRepository.findOne(attach);
            WanxiaoRechargeResult result = wanxiaoPayService.recharge(byOutTradeNo, one);
            if (result.isError()) {
                throw new RuntimeException(result.getReturnMessage());
            }
        }
        return "<xml>" +
                "   <return_code><![CDATA[SUCCESS]]></return_code>" +
                "   <return_msg><![CDATA[OK]]></return_msg>" +
                "</xml>";
    }

    private String findAttach(String notifyXml) {
        Pattern pattern = Pattern.compile("<attach><!\\[CDATA\\[(.+)\\]\\]></attach>");
        Matcher matcher = pattern.matcher(notifyXml);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return null;
    }

    private String readXmlFromStream(HttpServletRequest request) throws IOException {
        ServletInputStream inputStream = request.getInputStream();
        InputStreamReader reader = new InputStreamReader(inputStream);
        BufferedReader bufferedReader = new BufferedReader(reader);
        StringBuilder xmlBuilder = new StringBuilder();
        String s = bufferedReader.readLine();
        while (!StringUtils.isEmpty(s)) {
            xmlBuilder.append(s);
            s = bufferedReader.readLine();
        }
        return xmlBuilder.toString();
    }

    public String findOutTradeNo(String notifyXml) {
        Pattern pattern = Pattern.compile("<out_trade_no><!\\[CDATA\\[(.+)\\]\\]></out_trade_no>");
        Matcher matcher = pattern.matcher(notifyXml);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return null;
    }

    /**
     * 生成下单结果
     *
     * @param result 微信下单结果
     * @return 返回前台的结果
     */
    private RechargeResultDto buildResult(WechatUnifiedOrderResult result) {
        RechargeResultDto resultDto = new RechargeResultDto(result);
        resultDto.setAppid(properties.getAppid());
        resultDto.setPaySign(buildSign(resultDto));
        return resultDto;
    }

    /**
     * 调用微信统一下单接口
     * 在统一下单成功后,需要记录到数据库中
     *
     * @param parameter 统一下单参数
     * @return 统一下单结果
     */
    private WechatUnifiedOrderResult unifiedOrder(WechatUnifiedOrderRequestParameter parameter) throws JAXBException {
        WechatUnifiedOrderResult result = null;
        parameter.setSign(buildSign(parameter));
        //生成sign
        buildSign(parameter);
        //将参数转换为xml
        String xmlAsString = XmlUtil.objectToXML(parameter);
        HttpEntity<String> httpEntity = new HttpEntity<>(xmlAsString);
        String resultString = template.postForEntity(properties.getUnifiedorder(), httpEntity, String.class).getBody();
        //解析返回结果
        if (!StringUtils.isEmpty(resultString)) {
            result = XmlUtil.xmlToObject(WechatUnifiedOrderResult.class, resultString);
        }


        return result;
    }

    private String buildSign(ParameterToMap parameter) {
        String s = parameter.toUrlString();
        String stringSignTemp = String.format("%s&key=%s", s, properties.getSec());
        return MD5Utils.encode(stringSignTemp).toUpperCase();
    }

    /**
     * 根据玩校统一下单和玩校的token构建微信统一下单支付参数
     *
     * @param amount  金额
     * @param applyId 订单ID
     * @return 微信统一下单参数
     */
    private WechatUnifiedOrderRequestParameter createUnifiedorderRequestParameter(BigDecimal amount, String applyId) {
        return new WechatUnifiedOrderRequestParameter(properties, amount, applyId);
    }
}
